home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / vh-1_5.lha / vh-1.5 / browse.c next >
C/C++ Source or Header  |  1993-05-11  |  34KB  |  1,453 lines

  1. /*****************************************************************************
  2.  
  3. NAME
  4.    browse.c -- curses(3)-using display front end for vh database browser
  5.  
  6. SYNOPSIS
  7.    main(argc, argv)                   --- browser main sequence
  8.    int argc; char *argv[][];
  9.  
  10. DESCRIPTION
  11.    This module defines the main and browse loop for the vh browser;
  12. all user-interface decisions are made here.  Screen, keyboard and
  13. mouse handling support may be found in screen.c.
  14.  
  15. AUTHORS
  16.    Written by Eric S. Raymond <eric@snark.thyrsus.com> for a UNIX
  17. port of the 1.1 version of Raymond Gardner's MS-DOS browser, October
  18. 1991.  Please see the source distribution's READ.ME for license
  19. terms.
  20.  
  21. *****************************************************************************/
  22. /*LINTLIBRARY*/
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <signal.h>
  26. #include <ctype.h>
  27. #include <assert.h>
  28. #ifdef ATT
  29. #include <termio.h>
  30. #endif /* ATT */
  31.  
  32. #include "screen.h"
  33. #include "vh.h"
  34.  
  35. #ifndef CONST
  36. #define    const
  37. #endif /* CONST */
  38.  
  39. #ifdef MSDOS
  40. /* emulate Borland-style scrollbar */
  41. #define SBARCH        ACS_CKBOARD    /* scrollbar character */
  42. #define SBARTOPCH    30        /* scrollbar up arrow */
  43. #define SBARBOTCH    31        /* scrollbar down arrow */
  44. #else
  45. /* approximate X-style scrollbar */
  46. #define SBARCH        ACS_VLINE    /* scrollbar character */
  47. #define SBARTOPCH    ACS_TTEE    /* scrollbar up arrow */
  48. #define SBARBOTCH    ACS_BTEE    /* scrollbar down arrow */
  49. #endif
  50. #define SBARTHUMBCH    ACS_BULLET    /* scrollbar thumb character */
  51.  
  52. #ifdef A_COLOR
  53. #define NORMPAIR    1
  54. #define HILITEPAIR    2
  55. #define SELPAIR        3
  56. #define PROMPTPAIR    4
  57. #define FKEYPAIR    5
  58. #define SBARPAIR    6
  59. #define SBARENDPAIR    7
  60. #define SBTHUMBPAIR    8
  61. #endif /* A_COLOR */
  62.  
  63. #define    SUPPRESS    0
  64. #define OPTIONAL    1
  65. #define FORCED        2
  66.  
  67. #ifndef UNIX
  68. #define erasechar()    BS
  69. #endif /* UNIX */
  70.  
  71. #define CUTFILE    "%s.cut"
  72.  
  73. /*
  74.  * When paging forward or back, it helps the reader to page by a few less
  75.  * lines than the page depth, so a few of the old ones show to give context.
  76.  * This controls how many old lines will show.
  77.  */
  78. #define OVERLAP    4
  79.  
  80. /* dialogue/message window parameters */
  81. #define DTOP    10
  82. #define DLEFT    10
  83. #define DHEIGHT    7
  84. #define DWIDTH    42
  85.  
  86. /*******************************************************************
  87.  *
  88.  * All message texts declared here for internationalization purposes
  89.  *
  90.  ******************************************************************/
  91.  
  92. #ifdef MSDOS
  93. #define USAGE    "usage: jargon [-cigms] [-b key] [srcname] [indexname]\n\
  94.     -c --- check textfile/index pair for consistency\n\
  95.     -i --- enable incremental-lookup and search\n\
  96.     -g --- generate new index from textfile\n\
  97.     -m --- force monochrome operation\n\
  98.     -s --- compensate for snowy EGA monitor\n\
  99.     -b --- write single entry specified by following key to stdout\n\
  100.     -r --- display random entry\n"
  101. #define TBANNER    "TAB-Index  F1-help  F10-exit"
  102. #define IBANNER    "TAB-Text   F1-help  F10-exit"
  103. #else
  104. #define USAGE    "usage: jargon [-cigmr] [-b key] [srcname] [indexname]\n"
  105. #define TBANNER    "TAB-Index  ^Q-help   ^C-exit"
  106. #define IBANNER    "TAB-Text   ^Q-help   ^C-exit"
  107. #endif
  108. typedef struct
  109. {
  110.     int    left;    /* left boundary of pushbutton */
  111.     int right;    /* right boundary of pushbutton */
  112.     char cmd;    /* command character associated with pushbutton */
  113. }
  114. strip;
  115. static strip panel[] = {{0, 8, TAB}, {11, 17, CTL('Q')}, {21, 27, CTL('C')}};
  116. #define PANELSTART    (COLS - 1 - strlen((cf==&vhi) ? IBANNER : TBANNER) + 1)
  117.  
  118. #define NOREFS    "No references on this page"
  119. #define NOSRCH    "No previous search"
  120. #define CUTNOP    "Can't open cut file"
  121. #define CUTNOW    "Cutting to jargon.cut..."
  122. #define CUTDNE    "Cutting to jargon.cut...Done"
  123.  
  124. #define BRSMES    "Browsing %s...\n"
  125. #define IDXMES    "Generating index for %s...\n"
  126. #define CHKMES    "Performing consistency check for %s...\n"
  127.  
  128. /*
  129.  * These have to be declared static, not macros; we use the address of the
  130.  * current prompt as a mode indicator.
  131.  */
  132. const static char NOMESG[] =    "                   ";
  133. #ifndef POPUP
  134. /* we want these fixed-length so the entry line looks good */
  135. const static char SEARCH[] =    "String search for: ";
  136. const static char INSRCH[] =    "Search proceeding: ";
  137. const static char NOTFND[] =    "Search failed:     ";
  138. const static char LOOKUP[] =    "Looking up entry:  ";
  139. const static char LOOKING[] =    "Lookup in progress:";
  140. #else
  141. /* the popup windows will get sized to these */
  142. const static char SEARCH[] =    "String search for";
  143. const static char INSRCH[] =    "Search proceeding";
  144. const static char NOTFND[] =    "Search failed";
  145. const static char LOOKUP[] =    "Looking up entry";
  146. const static char LOOKING[] =    "Lookup in progress";
  147. const static char BDLOOK[] =    "Lookup failed";
  148. #endif /* POPUP */
  149.  
  150. const static char *help_screen[] =
  151. {
  152. "                          Jargon File Browser v. 1.5",
  153. "           Copyright 1991 by Raymond D. Gardner & Eric S. Raymond",
  154. "             All Rights Reserved; Free Redistribution Encouraged",
  155. "",
  156. "      ^L             redraw screen                      (Refresh)",
  157. "",
  158. "      space          page forward                       (PgDn)",
  159. "      ^U             page back                          (PgUp)",
  160. "      ^N / ^P        scroll forward / back              (Down/Up Arrow)",
  161. "      ^A / ^O        go to top / end of file            (Home/End)",
  162. "",
  163. "      ^J or ^M       chase highlighted reference        (Reference)",
  164. "      ^F / ^B        next/previous reference            (Right / Left Arrow)",
  165. "",
  166. "      ^E             lookup entry by name               (Select)",
  167. "      ^S             search for string                  (Find)",
  168. "      ^R             repeat previous search             (Shift-Find)",
  169. #ifdef UNIX /* keypad() seems to disable recognition of ESC */
  170. "      ^H             undo last search or lookup         (Undo)",
  171. #else
  172. "      ^H or ESC      undo last search or lookup         (Undo)",
  173. #endif
  174. "",
  175. "      ^I             toggle between word list and text  (Tab)",
  176. "      ^Y             append selected entry to cut file  (Print)",
  177. "",
  178. "      Typing an entry name followed by Enter works like an ^E command.",
  179. "                  Press any key to return to the browser.",
  180. NULL,
  181. };
  182.  
  183. /*******************************************************************
  184.  *
  185.  * Shared global data
  186.  *
  187.  ******************************************************************/
  188.  
  189. static char *execname;    /* name under which program was invoked */
  190. static FILEINFO    *cf;    /* pointer to current fileinfo block */
  191.  
  192. /* highlights */
  193. static chtype normattr, hiliteattr, selattr, promptattr, fkeyattr, sbarattr;
  194. static chtype sbarendattr, sbthumbattr;
  195.  
  196. static char lasthit[LNSZ + 1];    /* last key searched for */
  197. static char linbuf[LNSZ + 1];    /* line input scratch buffer */
  198. #ifdef POPUP
  199. static WINDOW *dwin;        /* dialogue & message window */
  200. #endif /* POPUP */
  201. static bool incrsearch;        /* do incremental search? */
  202.  
  203. /*******************************************************************
  204.  *
  205.  * Message and dialogue code
  206.  *
  207.  ******************************************************************/
  208.  
  209. static void waitforit(requeue)
  210. /* wait for keystroke or mouse click */
  211. bool    requeue;
  212. {
  213.     int    c;
  214. #ifdef MOUSE
  215.     int    hsy, hsx;
  216.  
  217.     /* wait for keypress or button click */
  218.     while ((c = egetch()) == KEY_MOUSE)
  219.     if (A_BUTTON_CHANGED & mouse_status(&hsy, &hsx))
  220.         break;
  221. #else
  222.     c = egetch();
  223. #endif /* MOUSE */
  224.     if (requeue)
  225.     ungetch(c);
  226. }
  227.  
  228. #if defined(POPUP) && defined(CURSES)
  229. static void draw_box(win, ty,tx,  by,bx)
  230. /* draw a box on the screen using best possible forms chars */
  231. WINDOW *win;
  232. int ty, tx, by, bx;
  233. {
  234.     int    j;
  235.  
  236.     mvwaddch(win, ty, tx, ACS_ULCORNER);
  237.     mvwaddch(win, by, bx, ACS_LRCORNER);
  238.     mvwaddch(win, by, tx, ACS_LLCORNER);
  239.     mvwaddch(win, ty, bx, ACS_URCORNER);
  240.     wmove(win, ty, tx+1);
  241.     for (j = tx + 1; j <= bx - 1; j++)
  242.     waddch(win, ACS_HLINE);
  243.     wmove(win, by, tx+1);
  244.     for (j = tx + 1; j <= bx - 1; j++)
  245.     waddch(win, ACS_HLINE);
  246.     for (j = ty + 1; j <= by - 1; j++)
  247.     { 
  248.     wmove(win, j, tx);
  249.     waddch(win, ACS_VLINE);
  250.     }
  251.     for (j = ty + 1; j <= by - 1; j++)
  252.     { 
  253.     wmove(win, j, bx);
  254.     waddch(win, ACS_VLINE);
  255.     }
  256.     wrefresh(win);
  257. }
  258.  
  259. static char *get_dialogue(prompt)
  260. /* get string from user, with prompts */
  261. char    *prompt;
  262. {
  263.     static char buf[MAXCOLS + 1];
  264.  
  265.     if (prompt == (char *)NULL)
  266.     {
  267.     delwin(dwin);
  268.     dwin = (WINDOW *)NULL;
  269.     }
  270.     else
  271.     {
  272.     /* create a dialogue window */
  273.     if (dwin == (WINDOW *)NULL)
  274.         dwin = newwin(DHEIGHT, DWIDTH, DTOP, DLEFT);
  275.  
  276.     wclear(dwin);
  277.  
  278.     draw_box(dwin, 0, 0, DHEIGHT-1, DWIDTH-1);
  279.     draw_box(dwin, 2, 1, DHEIGHT-3, DWIDTH-2);
  280.  
  281.     wattron(dwin, A_BOLD);
  282.     mvwaddstr(dwin, 1, 10, prompt);
  283.     wattroff(dwin, A_BOLD);
  284.  
  285.     echo();
  286.     mvwgetstr(dwin, 3, 3, buf);
  287.     noecho();
  288.  
  289.     return(buf);
  290.     }
  291. }
  292. #endif /* defined(POPUP) && defined(CURSES) */
  293.  
  294. static void message(s)
  295. /* write message to prompt line */
  296. char    *s;
  297. {
  298. #ifndef POPUP
  299.     attrset(promptattr);
  300.     mvaddstr(LINES-1, 0, s);
  301.     attrset(normattr);
  302.     refresh();
  303. #else
  304.     WINDOW    *mwin;
  305.  
  306.     if (dwin)
  307.     {
  308.     mvwaddstr(dwin, 5, 10, NOMESG);
  309.     wattron(dwin, A_BOLD);
  310.     mvwaddstr(dwin, 5, 10, s);
  311.     wattroff(dwin, A_BOLD);
  312.     wrefresh(dwin);
  313.     }
  314.     else
  315.     {
  316.     mwin = newwin(3, strlen(s) + 2, DTOP, DLEFT);
  317.     wclear(mwin);
  318.     draw_box(mwin, 0, 0, 2, strlen(s) + 1);
  319.  
  320.     wattron(mwin, A_BOLD);
  321.     mvwaddstr(mwin, 1, 1, s);
  322.     wattroff(mwin, A_BOLD);
  323.     wrefresh(mwin);
  324.  
  325.     waitforit(TRUE);
  326.  
  327.     overwrite(stdscr, mwin);
  328.     delwin(mwin);
  329.     touchline(DTOP, stdscr);
  330.     wrefresh(stdscr);
  331.     }
  332. #endif /* POPUP */
  333. }
  334.  
  335. /*******************************************************************
  336.  *
  337.  * The browser itself
  338.  *
  339.  ******************************************************************/
  340.  
  341. static void uninitbrowse(doclear)
  342. /* end the browse, either normally or due to signal */
  343. bool    doclear;
  344. {
  345.     mouse_hide();
  346.     if (doclear)
  347.     {
  348.     clear();
  349.     refresh();
  350.     }
  351. #ifdef CURSES
  352.     /* weird sex with the tty driver */
  353.     (void)resetterm();
  354.     (void)echo();
  355. #ifndef OLDCURSES
  356.     (void)flushinp();
  357. #endif /* OLDCURSES */
  358. #endif /* CURSES */
  359.     (void)endwin();
  360.     exit(0);
  361. }
  362.  
  363. static void paintselect(rp, attr)
  364. /* turn highlight on a selection on or off */
  365. region    *rp;    /* which to highlight */
  366. chtype    attr;    /* attribute to use */
  367. {
  368.     int    x, y;
  369.  
  370.     attrset(attr);
  371.     if (rp->yl == rp->yr)
  372.     {
  373.     move(rp->yl, rp->xl);
  374.     for (x = rp->xl; x <= rp->xr; x++)
  375.         addch(inch() & A_CHARTEXT);
  376.     }
  377.     else
  378.     {
  379.     int c, lastnsp;
  380.  
  381.     /* don't highlight trailing spaces on line-wrapped references */
  382.     for (lastnsp = COLS - 1; lastnsp > 0; lastnsp--)
  383.     {
  384.         move(rp->yl, lastnsp);
  385.         if ((inch() & A_CHARTEXT) != ' ')
  386.         break;
  387.     }
  388.  
  389.     /* write trailing-line part of highlight */
  390.     move(rp->yl, rp->xl);
  391.     for (x = rp->xl; x <= lastnsp; x++)
  392.         addch(inch() & A_CHARTEXT);
  393.  
  394.     /* highlight everything *between* 1st and last lines in region */
  395.     for (y = rp->yl + 1; y < rp->yr; y++)
  396.     {
  397.         move(y, x);
  398.         for (x = 0; x < COLS - 1; x++)
  399.         addch(inch() & A_CHARTEXT);
  400.     }
  401.  
  402.     /* don't highlight leading spaces on line-wrapped references */
  403.     for (x = 0; x <= COLS - 1; x++)
  404.     {
  405.         move(rp->yr, x);
  406.         if ((inch() & A_CHARTEXT) != ' ')
  407.         break;
  408.     }
  409.  
  410.     /* write leading-line part of highlight */
  411.     move(rp->yr, x);
  412.     for (; x <= rp->xr; x++)
  413.         addch(inch() & A_CHARTEXT);
  414.     }
  415.     attrset(normattr);
  416. }
  417.  
  418. static bool chase(x, y, isindex)
  419. /* try to chase a link at this location */
  420. int    x, y;
  421. bool    isindex;
  422. {
  423.     long pos;    /* find target */
  424.     int dummy;
  425.  
  426.     enqueue(cf);    /* make sure we can un-chase it */
  427.  
  428.     pos = iflink(x, y, &dummy, &dummy, isindex);
  429.     if (pos <= 0)
  430.     {
  431.     dequeue(cf);
  432.     return(FALSE);
  433.     }
  434.     else
  435.     {
  436.     if (isindex)    /* if index, switch to text */
  437.     {
  438.         cf = vht;
  439.         cf->dsptoppos = cf->dspnextpos = NOWHERE;
  440.     }
  441.  
  442.     cf->toppos = pos;    /* set for new display */
  443.     return(TRUE);
  444.     }
  445. }
  446.  
  447.  
  448. static bool search(str, c)
  449. /* search for given string in file */
  450. char    *str;
  451. {
  452.     int    i;
  453.  
  454.     message(INSRCH);
  455.     enqueue(cf);
  456.     if (c)
  457.     cf->hitpos = ifind(cf->fp, cf->dspnextpos, c);
  458.     else
  459.     cf->hitpos = ffind(cf->fp, cf->dspnextpos, str);
  460.     if (cf->hitpos == NOWHERE)
  461.     {
  462.     message(NOTFND);
  463.     dequeue(cf);
  464.     return(FALSE);
  465.     }
  466.     else
  467.     {
  468.     /* else display hit on fourth line */
  469.     cf->toppos = cf->hitpos;
  470.     for (i = 0; i < OVERLAP; i++)
  471.         cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
  472.     if (str != lasthit)
  473.         (void) strcpy(lasthit, str);
  474.     return(TRUE);
  475.     }
  476. }
  477.  
  478. static bool lookup(str, c)
  479. /* search for given entry in file */
  480. char    *str, c;
  481. {
  482.     long pos;            /* find target */
  483.  
  484.     enqueue(cf);
  485.  
  486.     if (c)
  487.     pos = ilocate(c);
  488.     else
  489.     {
  490.     message(LOOKING);
  491.     pos = xlocate(str);
  492.     }
  493.  
  494.     if (cf == &vhi)        /* if index, switch to text */
  495.     {
  496.     cf = vht;
  497.     cf->dsptoppos = cf->dspnextpos = cf->endpos;
  498.     }
  499.  
  500.     if (pos < 0)
  501.     {
  502.     cf->toppos = -pos;        /* set for new display */
  503.     return(FALSE);
  504.     }
  505.     else
  506.     {
  507.     cf->toppos = pos;        /* set for new display */
  508.     return(TRUE);
  509.     }
  510. }
  511.  
  512. static daddr_t byname(prompt, str, c)
  513. /* search or lookup given entry in file, depending on prompt value */
  514. char    *prompt, *str, c;
  515. {
  516.     if (prompt == SEARCH)
  517.     return(search(str, c));
  518.     else
  519.     return(lookup(str, c));
  520. }
  521.  
  522. /* entry buffer for lookup and search commands */
  523. static char    entrybuf[LNSZ + 1];
  524. static int    entrycnt = 0;
  525.  
  526. static void enterchar(c, prompt, row, col, maxcol)
  527. /* accept a character into the entry buffer */
  528. char    c;
  529. char    *prompt;
  530. int    row, col;
  531. int    maxcol;
  532. {
  533.     int    es;
  534.  
  535.     if (c == '\0')
  536.     {
  537.     entrybuf[entrycnt = 0] = '\0';
  538.     return;
  539.     }
  540.  
  541.     es = col + strlen(prompt);
  542.     attrset(promptattr);
  543.     if (c == erasechar())
  544.     {
  545.     if (entrycnt > 0)
  546.     {
  547.         mvaddch(row, es + --entrycnt, ' ');
  548.         entrybuf[entrycnt] = '\0';
  549.         move(row, es + entrycnt);
  550.         ilocate(BS);
  551.         if (incrsearch)
  552.         dequeue(cf);
  553.     }
  554.     }
  555. #ifdef CURSES
  556.     else if (c == killchar())
  557.     {
  558.     /* delete everything back to the end of the prompt */
  559.     while (entrycnt > 0)
  560.     {
  561.         mvaddch(row, es + --entrycnt, ' ');
  562.         entrybuf[entrycnt] = '\0';
  563.         move(row, es + entrycnt);
  564.         if (incrsearch)
  565.         dequeue(cf);
  566.     }
  567.     ilocate(DEL);
  568.     }
  569. #endif /* CURSES */
  570.     else if (isprint(c))
  571.     {
  572.     mvaddstr(row, col, prompt);
  573.     if (es + entrycnt < maxcol
  574.         && (!incrsearch || byname(prompt, (char *)NULL, c) != NOWHERE))
  575.     {
  576.         mvaddch(row, es + entrycnt, c);
  577.         entrybuf[entrycnt++] = c;
  578.         entrybuf[entrycnt] = '\0';
  579.     }
  580.     else if (c == '\0')
  581.         entrybuf[entrycnt = 0] = '\0';
  582.     else
  583.         beep();
  584.     }
  585.     attrset(normattr);
  586.  
  587.     refresh();
  588. }
  589.  
  590. static void cutentry(pos, fp)
  591. /* send the text of the entry starting at pos to the gin file pointer */
  592. daddr_t pos;
  593. FILE *fp;
  594. {
  595.     (void) fseek(vht->fp, pos, SEEK_SET);
  596.  
  597.     /* always want first line */
  598.     (void) fgets(linbuf, LNSZ, vht->fp);
  599.     (void) fputs(linbuf, fp);
  600.  
  601.     /* copy succeeding lines till we get to next entry */
  602.     while (fgets(linbuf, LNSZ, vht->fp) != (char *)NULL)
  603.     if (linbuf[0] == '=' || headword(linbuf))
  604.         break;
  605.     else
  606.         (void) fputs(linbuf, fp);
  607. }
  608.  
  609. static char *nblanks(n)
  610. /* blank-string return for fast fills; return up to 132 = MAXCOLS */
  611. int    n;
  612. {
  613.     /* MAXCOLS+1 blanks */
  614.     const static char blanks[] = "                                                                                                                                     ";
  615.     return(&blanks[sizeof(blanks) - 1 - n]);
  616. }
  617.  
  618. static void browse(name, cok)
  619. /* interactive browser loop */
  620. char    *name;
  621. bool    cok;
  622. {
  623.     int        i, c;
  624.     char    **hp;
  625.     bool    restore_select = FALSE;
  626.     FILE    *cutfp;
  627.     int        lightup = FORCED;
  628. #ifdef MOUSE
  629.     int        hsy, hsx, mstat;    /* mouse hot spot coordinates */
  630.     bool    thumbdrag = FALSE;    /* are we in a thumbdrag? */
  631. #endif /* MOUSE */
  632. #ifndef POPUP
  633.     char    *prompt = NOMESG;
  634. #else
  635.     char    *cp;
  636. #endif /* POPUP */
  637. #ifdef ATT
  638.     struct termio    tty;
  639.     int        saveintr;
  640. #endif /* ATT */
  641.  
  642.     if (!initbrowse(name))
  643.     exit(1);
  644.     cf = vht;
  645.  
  646. #ifdef UNIX
  647.     (void) signal(SIGINT,uninitbrowse);
  648.     (void) signal(SIGQUIT,uninitbrowse);
  649.     (void) signal(SIGTERM,uninitbrowse);
  650.     (void) signal(SIGIOT,uninitbrowse);        /* for assert(3) */
  651. #ifdef ATT
  652.     (void) ioctl(0, TCGETA, &tty);
  653.     saveintr = tty.c_cc[VINTR];
  654. #endif /* ATT */
  655. #endif /* UNIX */
  656.  
  657.     (void) initscr();
  658.     setlastpage();
  659.  
  660. #ifdef A_COLOR
  661.     if (cok = (cok && has_colors()))
  662.     {
  663.     start_color();
  664.  
  665.     init_pair(NORMPAIR, COLOR_WHITE, COLOR_BLUE);
  666.     init_pair(HILITEPAIR, COLOR_RED, COLOR_WHITE);
  667.     init_pair(SELPAIR, COLOR_YELLOW, COLOR_BLUE);
  668.     init_pair(PROMPTPAIR, COLOR_BLACK, COLOR_CYAN);
  669.     init_pair(FKEYPAIR, COLOR_RED, COLOR_CYAN);
  670.     init_pair(SBARPAIR, COLOR_WHITE, COLOR_BLACK);
  671.     init_pair(SBARENDPAIR, COLOR_BLUE, COLOR_WHITE);
  672.     init_pair(SBTHUMBPAIR, COLOR_BLUE, COLOR_BLACK);
  673.     }
  674.     mouse_color(cok);
  675.  
  676.     normattr = cok ? (COLOR_PAIR(NORMPAIR) | A_BOLD) : A_NORMAL;
  677.     hiliteattr = cok ? COLOR_PAIR(HILITEPAIR) : A_REVERSE;
  678.     selattr = cok ? (COLOR_PAIR(SELPAIR) | A_BOLD) : A_BOLD;
  679.     promptattr = cok ? COLOR_PAIR(PROMPTPAIR) : A_REVERSE;
  680.     fkeyattr = cok ? COLOR_PAIR(FKEYPAIR) : A_BOLD;
  681. #ifndef MSDOS
  682.     sbarendattr = sbthumbattr = 
  683.     sbarattr = cok ? COLOR_PAIR(SBARPAIR) : A_NORMAL;
  684. #else
  685.     sbarattr = cok ? COLOR_PAIR(SBARPAIR) : A_REVERSE;
  686.     sbarendattr = cok ? (COLOR_PAIR(SBARENDPAIR) | A_BOLD) : A_REVERSE;
  687.     sbthumbattr = cok ? (COLOR_PAIR(SBTHUMBPAIR) | A_BOLD) : A_NORMAL;
  688. #endif
  689. #else
  690.     normattr = A_NORMAL;
  691.     hiliteattr = A_REVERSE;
  692.     selattr = A_BOLD;
  693.     promptattr = A_REVERSE;
  694.     fkeyattr = A_BOLD;
  695.     sbarendattr = sbthumbattr = 
  696.     sbarattr = A_NORMAL;
  697. #ifdef MSDOS
  698.     sbarattr = A_REVERSE;
  699.     sbarendattr = A_REVERSE;
  700.     sbthumbattr = A_NORMAL;
  701. #endif /* MSDOS */
  702. #endif /* A_COLOR */
  703.  
  704.     mouse_init();
  705. #ifdef MOUSE
  706.     mouse_move(LINES - 1, COLS - 1);
  707. #endif /* MOUSE */
  708.  
  709. #ifdef CURSES
  710.     (void)saveterm();
  711.     (void)nonl();
  712.     (void)raw();
  713.     (void)noecho();
  714. #ifndef OLDCURSES
  715.     (void)keypad(stdscr, TRUE);
  716. #endif /* BSD */
  717. #endif /* CURSES */
  718.  
  719. #ifdef ATT
  720.     (void) ioctl(0, TCGETA, &tty);
  721.     tty.c_cc[VINTR] = saveintr;
  722.     tty.c_iflag |= BRKINT;
  723.     tty.c_iflag &=~ IGNBRK;
  724.     tty.c_lflag |= ISIG;
  725.     (void) ioctl(0, TCSETA, &tty);
  726. #endif /* ATT */
  727.  
  728.     lasthit[0] = '\0';
  729.  
  730.     for (c = KEY_REFRESH; c != CTL('C') && c != KEY_EXIT; c = egetch())
  731.     {
  732. #ifdef DEBUG
  733.     if (isprint(c))
  734.         (void) fprintf(stderr, "command: %c\n", c);
  735.     else if (iscntrl(c))
  736.         (void) fprintf(stderr, "command: ^%c\n", c + '@');
  737.     else if (c >= 0x80 & c <= 0x9f)
  738.         (void) fprintf(stderr, "command: M-^%c\n", (c &~ 0x80) + '@');
  739.     else if (c >= 0x80)
  740.         (void) fprintf(stderr, "command: M-%c\n", c &~ 0x80);
  741.     else
  742.         (void) fprintf(stderr, "command: 0x%02x\n", c);
  743. #endif /* DEBUG */
  744.  
  745.     switch(c)
  746.     {
  747. #ifdef MSDOS
  748.     case KEY_F(10):
  749.         (void) ungetch(KEY_EXIT);
  750.         break;
  751. #endif /* MSDOS */
  752.  
  753. #ifndef OLDCURSES
  754.     case KEY_HELP:        /* go to help screen */
  755. #endif /* OLDCURSES */
  756.     case CTL('Q'):
  757. #ifdef MSDOS
  758.     case KEY_F(1):
  759. #endif /* MSDOS */
  760.         restore_select = TRUE;
  761.         i = 0;
  762.         for (hp = help_screen; *hp; hp++)
  763.         {
  764.         mvaddstr(i++, 0, *hp);
  765.         addstr(nblanks(COLS - strlen(*hp)));
  766.         }
  767.         refresh();
  768.         cf->dsptoppos = NOWHERE;
  769.         waitforit(FALSE);
  770.         /* FALL THROUGH */
  771.  
  772. #ifndef OLDCURSES
  773.     case KEY_REFRESH:    /* redraw screen */
  774. #endif /* OLDCURSES */
  775.     case CTL('L'):
  776.         restore_select = TRUE;
  777. #ifdef CURSES
  778.         clearok(stdscr, TRUE);
  779. #endif /* CURSES */
  780.         break;
  781.  
  782.     case SP:
  783.         restore_select = FALSE;
  784. #ifndef POPUP
  785.         if (prompt != NOMESG)
  786.         goto medialspace;
  787.         /* FALL THROUGH */
  788. #endif /* POPUP */
  789.  
  790. #ifndef OLDCURSES
  791.     case KEY_NPAGE:        /* page forward */
  792. #endif /* OLDCURSES */
  793.     pagefwd:
  794.         enqueue(cf);
  795.         restore_select = FALSE;
  796.         for (i = 0; i < LINES - OVERLAP; i++)
  797.         {
  798.         if (cf->toppos == cf->lastpagetoppos)
  799.             break;
  800.         cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
  801.         }
  802.         if (cf->sel.xl != NOPLACE)
  803.         {
  804.         cf->sel.yl -= i; cf->sel.yr -= i;
  805.         if (!(restore_select = (cf->sel.yl >= 0)))
  806.             cf->sel.xl = NOPLACE;
  807.         }
  808.         break;
  809.  
  810. #ifndef OLDCURSES
  811.     case KEY_PPAGE:    /* page back */
  812. #endif /* OLDCURSES */
  813.     case CTL('U'):
  814.     pagebak:
  815.         enqueue(cf);
  816.         restore_select = FALSE;
  817.         for (i = 0; i < LINES - OVERLAP; i++)
  818.         {
  819.         if (cf->toppos == 0)
  820.             break;
  821.         cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
  822.         }
  823.         if (cf->sel.xl != NOPLACE)
  824.         {
  825.         cf->sel.yl += i; cf->sel.yr += i;
  826.         if (!(restore_select = (cf->sel.yr < LINES - 1)))
  827.             cf->sel.xl = NOPLACE;
  828.         }
  829.         break;
  830.  
  831. #ifndef OLDCURSES
  832.     case KEY_UP:
  833. #endif /* OLDCURSES */
  834.     case CTL('N'):
  835. linebak:
  836.         restore_select = FALSE;
  837.         if (cf->toppos)
  838.         {
  839.         cf->toppos = getprevln(cf->fp, cf->toppos, linbuf);
  840.         if (cf->sel.xl != NOPLACE)
  841.         {
  842.             cf->sel.yl++; cf->sel.yr++;
  843.             if (!(restore_select = (cf->sel.yr < LINES - 1)))
  844.             cf->sel.xl = NOPLACE;
  845.         }
  846.         }
  847.         break;
  848.  
  849. #ifndef OLDCURSES
  850.     case KEY_DOWN:
  851. #endif /* OLDCURSES */
  852.     case CTL('P'):
  853. linefwd:
  854.         restore_select = FALSE;
  855.         if (cf->toppos < cf->lastpagetoppos)
  856.         {
  857.         cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
  858.         if (cf->sel.xl != NOPLACE)
  859.         {
  860.             cf->sel.yl--; cf->sel.yr--;
  861.             if (!(restore_select = (cf->sel.yl >= 0)))
  862.             cf->sel.xl = NOPLACE;
  863.         }
  864.         }
  865.         break;
  866.  
  867. #ifndef OLDCURSES
  868.     case KEY_ENTER:        /* accept keyboard input */
  869. #endif /* OLDCURSES */
  870.     case CTL('J'):
  871.     case CTL('M'):
  872.         lightup = FORCED;
  873. #ifndef POPUP
  874.         restore_select = FALSE;
  875.         if (prompt == SEARCH || prompt == LOOKUP)
  876.         {
  877.         if (!incrsearch && byname(prompt, entrybuf, '\0') == NOWHERE)
  878.             beep();
  879.         enterchar('\0', LOOKUP, LINES - 1, 0, PANELSTART);
  880.         prompt = NOMESG;
  881.         break;
  882.         }
  883.         /* nothing in prompt buffer, no search active: FALL THROUGH */
  884. #endif /* POPUP */
  885.  
  886. #ifndef OLDCURSES
  887.     case KEY_REFERENCE:    /* chase reference */
  888. #endif /* OLDCURSES */
  889.         restore_select = FALSE;
  890.         if (cf->sel.xl == NOPLACE)    /* any link? */
  891.         {
  892.         message(NOREFS);
  893.         continue;
  894.         }
  895.         else
  896.         (void) chase(cf->sel.xl, cf->sel.yl, cf == &vhi);
  897.         break;
  898.  
  899. #ifndef OLDCURSES
  900.     case KEY_RIGHT:        /* next reference */
  901. #endif /* OLDCURSES */
  902.     case CTL('F'):
  903.         restore_select = FALSE;
  904.         if (cf->sel.xl == NOPLACE)
  905.         message(NOREFS);
  906.         else
  907.         {
  908.         paintselect(&cf->sel, (cf == &vhi) ? normattr : selattr);
  909.         cf->sel = findnextsel(cf->sel.xl, cf->sel.yl, cf == &vhi);
  910.         paintselect(&cf->sel, hiliteattr);
  911.         }
  912.         refresh();
  913.         continue;
  914.  
  915. #ifndef OLDCURSES
  916.     case KEY_LEFT:        /* previous reference */
  917. #endif /* OLDCURSES */
  918.     case CTL('B'):
  919.         restore_select = FALSE;
  920.         if (cf->sel.xl == NOPLACE)
  921.         message(NOREFS);
  922.         else
  923.         {
  924.         paintselect(&cf->sel, (cf == &vhi) ? normattr : selattr);
  925.         /*
  926.          * The -1 offset on xsel is important.  It insures that
  927.          * when findprevsel() starts looking for a previous
  928.          * tag, it doesn't find the current one again.
  929.          */
  930.         cf->sel = findprevsel(cf->sel.xl-1, cf->sel.yl, cf == &vhi);
  931.         paintselect(&cf->sel, hiliteattr);
  932.         }
  933.         refresh();
  934.         continue;
  935.  
  936. #ifndef OLDCURSES
  937.     case KEY_FIND:        /* search for string */
  938. #endif /* OLDCURSES */
  939.     case CTL('S'):
  940.         restore_select = FALSE;
  941. #ifndef POPUP
  942.         prompt = SEARCH;
  943. #else
  944.         if (cp = get_dialogue(SEARCH))
  945.         if (search(cp, '\0') != NOWHERE)
  946.         {
  947.             get_dialogue((char *)NULL);
  948.             break;
  949.         }
  950.         else
  951.             continue;
  952. #endif /* POPUP */
  953.         break;
  954.  
  955. #ifndef OLDCURSES
  956.      case KEY_SFIND:        /* search for last string again */
  957. #endif /* OLDCURSES */
  958.         case CTL('R'):
  959.         restore_select = FALSE;
  960.         if (lasthit[0] == '\0')
  961.         {
  962.         message(NOSRCH);
  963.         continue;
  964.         }
  965.         else
  966. #ifdef POPUP
  967.         (void) search(lasthit, '\0');
  968. #else
  969.         {
  970.             prompt = SEARCH;
  971.             (void) strcpy(entrybuf, lasthit);
  972.             entrycnt = strlen(lasthit);
  973.             (void) ungetch(CTL('J'));
  974.         }
  975. #endif /* POPUP */
  976.         break;
  977.  
  978. #ifndef OLDCURSES
  979.     case KEY_SELECT:    /* search for entry */
  980. #endif /* OLDCURSES */
  981.     case CTL('E'):
  982.     lookfor:
  983.         restore_select = FALSE;
  984. #ifndef POPUP
  985.         prompt = LOOKUP;
  986. #else
  987.         if (cp = get_dialogue(LOOKUP))
  988.         {
  989.         if (lookup(cp, '\0') == NOWHERE)
  990.             beep();
  991.         get_dialogue((char *)NULL);
  992.         break;
  993.         }
  994. #endif /* POPUP */
  995.         break;
  996.  
  997. #ifndef OLDCURSES
  998.     case KEY_HOME:        /* go to beginning of file */
  999.     case KEY_BEG:
  1000. #endif /* OLDCURSES */
  1001.     case CTL('A'):
  1002.         enqueue(cf);
  1003.         restore_select = FALSE;
  1004.         cf->toppos = 0;
  1005.         break;
  1006.  
  1007. #ifndef OLDCURSES
  1008.     case KEY_END:        /* go to end of file */
  1009. #endif /* OLDCURSES */
  1010.     case CTL('O'):
  1011.         enqueue(cf);
  1012.         restore_select = FALSE;
  1013.         cf->toppos = cf->lastpagetoppos;
  1014.         break;
  1015.  
  1016.     case BS:        /* this is context-sensitive */
  1017. #ifndef POPUP
  1018.         if (prompt != NOMESG)
  1019.         goto medialspace;
  1020.         /* FALL THROUGH */
  1021. #endif /* POPUP */
  1022.  
  1023. #ifndef OLDCURSES
  1024.     case KEY_UNDO:        /* backtrack into the location stack */
  1025. #endif /* OLDCURSES */
  1026.     case ESC:
  1027.         restore_select = TRUE;
  1028.         dequeue(cf);
  1029.         break;
  1030.  
  1031.     case TAB:        /* toggle between text and index */
  1032.         restore_select = TRUE;
  1033.         vhi.dsptoppos = vht->dsptoppos = NOWHERE;
  1034.         if (cf == vht)
  1035.         cf = &vhi;
  1036.         else
  1037.         cf = vht;
  1038.         break;
  1039.  
  1040. #ifndef OLDCURSES
  1041.     case KEY_PRINT:        /* append selected entry to file */
  1042. #endif /* OLDCURSES */
  1043.     case CTL('Y'):
  1044.         restore_select = FALSE;
  1045.         (void) sprintf(linbuf, CUTFILE, execname);
  1046.         if (cf->sel.xl == NOPLACE)
  1047.         {
  1048.         message(NOREFS);
  1049.         continue;
  1050.         }
  1051.         else if ((cutfp = fopen(linbuf, "a")) == (FILE *)NULL)
  1052.         {
  1053.         message(CUTNOP);
  1054.         continue;
  1055.         }
  1056.         else
  1057.         {
  1058.         long pos;    /* find target */
  1059.         int dummy;
  1060.  
  1061.         message(CUTNOW);
  1062.  
  1063.         pos = iflink(cf->sel.xl, cf->sel.yl, &dummy,&dummy, cf == &vhi);
  1064.         if (pos >= 0)    /* how can this fail? */
  1065.             cutentry(pos, cutfp);
  1066.         (void) fclose(cutfp);
  1067.  
  1068.         message(CUTDNE);
  1069.         continue;
  1070.         }
  1071.         break;
  1072.  
  1073. #ifdef MOUSE
  1074.     /*
  1075.      * Note: we treat a single-button mouse as having the left
  1076.      * button only.
  1077.      *
  1078.      * The mouse_status() function should return the key-status defines
  1079.      * implied below when appropriate.  It should return the zero-origin
  1080.      * mouse hotspot coordinates at character-cell resolution, with
  1081.      * CMDLINE and THUMBCOL as special values designating the command
  1082.      * line and thumb column respectively (on character displays these
  1083.      * would be LINES - 1 and COLS - 1 respectively).
  1084.      */
  1085.     case KEY_MOUSE:    /* mouse gesture has occurred */
  1086.         mstat = mouse_status(&hsy, &hsx);
  1087. #ifndef POPUP
  1088.         hsy = (hsy == LINES - 1) ? CMDLINE : hsy;
  1089. #endif /* POPUP */
  1090.         switch(mstat)
  1091.         {
  1092.         case BUTTON1_PRESSED:    /* left button down; chase link, else page */
  1093.         restore_select = FALSE;
  1094.         if (hsy == CMDLINE)        /* clicked on entry line */
  1095.         {
  1096.             hsx -= PANELSTART;
  1097.             for (i = 0; i < sizeof(panel) / sizeof(strip); i++)
  1098.             if (hsx >= panel[i].left && hsx <= panel[i].right)
  1099.                 (void) ungetch(panel[i].cmd);
  1100.             continue;
  1101.         }
  1102.         else if (hsx == THUMBCOL)    /* click in thumb column */
  1103.             {
  1104.             if (hsy == 0)        /* clicked top of bar */
  1105.             goto linebak;
  1106.             else if (hsy == LASTLINE)    /* clicked bottom of bar */
  1107.             goto linefwd;
  1108.             else if (hsy < cf->ythumb)    /* clicked above thumb */
  1109.             goto pagebak;
  1110.             else if (hsy > cf->ythumb)    /* clicked below thumb */
  1111.             goto pagefwd;
  1112.             else            /* clicked on the thumb */
  1113.             thumbdrag = TRUE;
  1114.             }
  1115.         else if (!chase(hsx, hsy, cf == &vhi))
  1116.             goto pagefwd;    /* click in text somewhere */
  1117.         break;
  1118.  
  1119.         case BUTTON1_RELEASED:    /* left button up */
  1120.         restore_select = FALSE;
  1121.         if (thumbdrag && 0 < hsy && hsy < LASTLINE)
  1122.         {
  1123.             cf->toppos =        /* compute new position */
  1124.             cf->lastpagetoppos *
  1125.             (hsy - 1) / (LASTLINE - 2);
  1126.  
  1127.             assert(0 <= cf->toppos &&
  1128.                cf->toppos <= cf->lastpagetoppos);
  1129.  
  1130.             /* ensure on line boundary */
  1131.             if (0 < cf->toppos && cf->toppos < cf->lastpagetoppos)
  1132.             cf->toppos = getnextln(cf->fp, cf->toppos, linbuf);
  1133.         }
  1134.         thumbdrag = FALSE;            
  1135.         break;
  1136.  
  1137.         case BUTTON3_PRESSED:    /* right button down; backtrack */
  1138.         restore_select = TRUE;
  1139.         dequeue(cf);
  1140.         break;
  1141.  
  1142.         case BUTTON3_RELEASED:    /* right button up */
  1143.         /* reserved for future expansion */
  1144.         break;
  1145.         }
  1146.         break;
  1147. #endif /* MOUSE */
  1148.  
  1149.     default:
  1150. #ifdef POPUP
  1151.         if (isprint(c))
  1152.         {
  1153.         (void) ungetch(c);
  1154.         goto lookfor;
  1155.         }            
  1156. #else
  1157.     medialspace:
  1158.         restore_select = FALSE;
  1159.         if (prompt == NOMESG)
  1160.         {
  1161.         ungetch(c);
  1162.         goto lookfor;
  1163.         }            
  1164.         enterchar(c, prompt, LINES - 1, 0, PANELSTART);
  1165. #endif /* POPUP */
  1166.         lightup = SUPPRESS;
  1167.     }
  1168.  
  1169. #ifndef POPUP
  1170.     /* regenerate prompt */
  1171.     attrset(promptattr);
  1172.     move(LINES - 1, 0);
  1173.     addstr(prompt);
  1174.     addstr(entrybuf);
  1175.     addstr(nblanks(PANELSTART - strlen(prompt) - strlen(entrybuf)));
  1176.     mvaddstr(LINES - 1, PANELSTART, (cf == &vhi) ? IBANNER : TBANNER);
  1177.     attrset(normattr);
  1178. #endif /* POPUP */
  1179.  
  1180.     if (cf->toppos != cf->dsptoppos)
  1181.     {
  1182.         int i;
  1183.         long newpos = cf->toppos;
  1184.  
  1185. #ifdef DEBUG
  1186.         (void) fprintf(stderr,
  1187.                "screen update triggered, toppos = %ld\n",
  1188.                cf->toppos);
  1189. #endif /* DEBUG */
  1190.  
  1191.         attrset(normattr);
  1192.         for (i = 0; i <= LASTLINE; i++)
  1193.         if ((newpos = getnextln(cf->fp, newpos, linbuf)) != NOWHERE)
  1194.         {
  1195.             mvaddstr(i, 0, linbuf);
  1196.             /*
  1197.              * This is actually faster, and creates less
  1198.              * flicker, than clearing the screen.
  1199.              */
  1200.             addstr(nblanks(COLS - strlen(linbuf)));
  1201.         }
  1202.         cf->dspnextpos = ftell(cf->fp);
  1203.  
  1204. #ifdef SCROLLBAR
  1205.         /*
  1206.          * Generate the scroll bar.  Yes, we really do want the denominator
  1207.          * to be lastpagetoppos and not endpos.  This means the thumb only
  1208.          * bottoms out if we're really at the end.
  1209.          */
  1210.         cf->ythumb = 1 + cf->toppos * (LASTLINE - 2) / cf->lastpagetoppos;
  1211.         assert(0 < cf->ythumb && cf->ythumb < LASTLINE);
  1212.         mvaddch(0, COLS, SBARTOPCH | sbarendattr);
  1213.         for (i = 1; i < LASTLINE; i++)     /* set up scrollbar */
  1214.         mvaddch(i, COLS, SBARCH | sbarattr);
  1215.         mvaddch(LASTLINE, COLS, SBARBOTCH | sbarendattr);
  1216.         mvaddch(cf->ythumb, COLS, SBARTHUMBCH | sbthumbattr);
  1217. #endif /* SCROLLBAR */
  1218.     }
  1219.  
  1220.     /*
  1221.      * This has to be done whether or not lightup is going to happen.
  1222.      * Otherwise, a following command may enqueue a bogus selection
  1223.      * box as part of state.
  1224.      */
  1225.     cf->sel = findnextsel(0, -1, cf == &vhi);
  1226.  
  1227.     if (lightup == SUPPRESS)
  1228.         lightup = OPTIONAL;
  1229.     else 
  1230.     {
  1231.         if (incrsearch)
  1232.         (void) byname(prompt, (char *)NULL, DEL);
  1233.  
  1234.         if (lightup == FORCED || cf->toppos != cf->dsptoppos)
  1235.         {
  1236.         lightup = OPTIONAL;
  1237.  
  1238.         /* highlight the current select, if any */
  1239.         if (restore_select && cf->sel.xl != NOPLACE)
  1240.             paintselect(&cf->sel, hiliteattr); /* restore existing select */
  1241.         else
  1242.         {
  1243.             /* highlight the first selection, if there is one */
  1244.             if (cf->sel.xl != NOPLACE)
  1245.             paintselect(&cf->sel, hiliteattr);
  1246.         }
  1247.  
  1248.         /* if in text, boldface all potential selects */
  1249.         if (!(cf == &vhi) && cf->sel.xl != NOPLACE)
  1250.         {
  1251.             region    hibox;
  1252.  
  1253. #ifdef DEBUG
  1254.             (void) fprintf(stderr,
  1255.                    "current: x = %2d, y = %2d, pos = %ld.\n",
  1256.                    cf->sel.xl, cf->sel.yl, cf->toppos);
  1257. #endif /* DEBUG */
  1258.             hibox.xl = cf->sel.xl;
  1259.             hibox.yl = cf->sel.yl;
  1260.             for (;;)
  1261.             {
  1262.             hibox = findnextsel(hibox.xl, hibox.yl, cf == &vhi);
  1263. #ifdef DEBUG
  1264.             (void) fprintf(stderr,
  1265.                        "hibox: x = %2d, y = %2d.\n",
  1266.                        hibox.xl, hibox.yl);
  1267. #endif /* DEBUG */
  1268.             if (hibox.xl == cf->sel.xl && hibox.yl == cf->sel.yl)
  1269.                 break;
  1270.             paintselect(&hibox, selattr);
  1271.             }
  1272.         }
  1273.         }
  1274.     }
  1275.  
  1276.     move(LINES - 1, 0);
  1277.  
  1278.     cf->dsptoppos = cf->toppos;
  1279.  
  1280.     refresh();
  1281.     }
  1282.  
  1283.     uninitbrowse(TRUE);
  1284. }
  1285.  
  1286. #ifdef MSDOS
  1287. static char *basename(s)
  1288. /* isolate base name of fully qualified filename (modifies the name passed!) */
  1289. char    *s;
  1290. {
  1291.     char *p;
  1292.     p = strrchr(s, '.');    /* find rightmost dot */
  1293.     if ( strchr(p, '\\') )    /* if a \ comes after it, find string end */
  1294.     p = strchr(p, 0);
  1295.     *p = 0;            /* terminate at last dot after a \ (if any)*/
  1296.     if ( (p = strrchr(s, '\\')) == NULL ) /* find last \ (if any) */
  1297.     p = s;            /* take whole string if no \ */
  1298.     else
  1299.     ++p;            /* else take string after \ */
  1300.     strlwr(p);
  1301.     return p;
  1302. }
  1303. #else
  1304. #define basename(x)    x    /* don't need to strip argv[0] under UNIX */
  1305. #endif /* MSDOS */
  1306.  
  1307. /*******************************************************************
  1308.  *
  1309.  * Main sequence
  1310.  *
  1311.  ******************************************************************/
  1312.  
  1313. main(argc, argv)
  1314. int     argc;
  1315. char    **argv;
  1316. {
  1317.     char    *dbname, *batchkey = NULL;
  1318.     int        i;
  1319.     bool    doindex = FALSE, docheck = FALSE, dofilter = FALSE;
  1320.     bool    forcemono = FALSE, atrandom = FALSE;
  1321.  
  1322.     execname = argv[0];
  1323.     while ((++argv, --argc) && *argv[0] == '-')
  1324.     for (i = 1; argv[0][i]; i++)
  1325.         switch(argv[0][i])
  1326.         {
  1327.         case 'b':
  1328.         batchkey = argv[1];
  1329.         break;
  1330.  
  1331.         case 'c':
  1332.         docheck = TRUE;
  1333.         break;
  1334.  
  1335.         case 'f':
  1336.         dofilter = TRUE;
  1337.         break;
  1338.  
  1339.         case 'g':
  1340.         doindex = TRUE;
  1341.         break;
  1342.  
  1343.         case 'i':
  1344.         incrsearch = TRUE;
  1345.         break;
  1346.  
  1347.         case 'm':
  1348.         forcemono = TRUE;
  1349.         break;
  1350.  
  1351.         case 'r':
  1352.         atrandom = TRUE;
  1353.         break;
  1354.  
  1355. #ifdef __TURBOC__
  1356.         case 's':
  1357.             {   /* if CGA that snows: */
  1358.             extern bool has_snowy_CGA;
  1359.             has_snowy_CGA = TRUE;
  1360.             }
  1361.         break;
  1362. #endif /* __TURBOC__ */
  1363.  
  1364.         default:
  1365.         (void) fprintf(stderr, USAGE);
  1366.         exit(1);
  1367.         }
  1368.  
  1369. #ifdef DEBUG
  1370.     (void) freopen("ERRLOG", "w", stderr);
  1371. #endif /* DEBUG */
  1372.  
  1373.     /*
  1374.      * User specified a batch-retrieval option.
  1375.      * OK, snarf the keyword from the next argument.
  1376.      */
  1377.     if (batchkey)
  1378.     ++argv, --argc;
  1379.  
  1380.     dbname = argc ? argv[0] : basename(execname);
  1381.  
  1382.     if (!doindex && !docheck && !atrandom && !dofilter
  1383.     && batchkey == (char *)NULL)
  1384.     {
  1385.     (void) printf(BRSMES, dbname);
  1386.     browse(dbname, !forcemono);
  1387.     }
  1388.  
  1389.     if (doindex)
  1390.     {
  1391.     (void) printf(IDXMES, dbname);
  1392.     if (argc)
  1393.         mkindex(argc, argv);
  1394.     else
  1395.         mkindex(1, &dbname);
  1396.     }
  1397.  
  1398.     if (docheck)
  1399.     {
  1400.     (void) printf(CHKMES, dbname);
  1401.     chkindex(dbname);
  1402.     }
  1403.  
  1404.     if (batchkey)
  1405.     {
  1406.     daddr_t    pos;
  1407.  
  1408.     if (!initbrowse(dbname))
  1409.         exit(1);
  1410.  
  1411.     if ((pos = xlocate(batchkey)) >= 0)
  1412.         cutentry(pos, stdout);
  1413.     }
  1414.  
  1415.     if (dofilter)
  1416.     {
  1417.     daddr_t    pos;
  1418.     char    batchkey[81];
  1419.  
  1420.     if (!initbrowse(dbname))
  1421.         exit(1);
  1422.  
  1423.     while (fgets(batchkey, sizeof(batchkey) - 1, stdin) != (char *)NULL)
  1424.     {
  1425.         char    *ep = &batchkey[strlen(batchkey) - 1];
  1426.  
  1427.         if (*ep == '\n')
  1428.         {
  1429.         *ep = '\0';
  1430.         if (*--ep == '\r')
  1431.             *ep = '\0';
  1432.         }
  1433.         if ((pos = xlocate(batchkey)) >= 0)
  1434.         cutentry(pos, stdout);
  1435.     }
  1436.     }
  1437.  
  1438.     if (atrandom)
  1439.     {
  1440.     daddr_t    pos;
  1441.  
  1442.     if (!initbrowse(dbname))
  1443.         exit(1);
  1444.  
  1445.     if ((pos = jrandom()) >= 0)
  1446.         cutentry(pos, stdout);
  1447.     }
  1448.  
  1449.     exit(0);
  1450. }
  1451.  
  1452. /* browse.c ends here */
  1453.